package org.codetranslators.compiler.tiger.semantics;

import org.codetranslators.common.Temp;
import org.codetranslators.compiler.tiger.abssyntree.Symbol;

abstract class Entry {

}

class TypeEntry extends Entry
{
	public TypeEntry actual() 
	{
		return this;
	}
}

/**
 * 
 * Singleton class for the type entry for int
 *
 */

class IntTypeEntry extends TypeEntry
{
	private static IntTypeEntry singleInstance;
	
	private IntTypeEntry(){	}
	
	public static IntTypeEntry getInstance()
	{
		if(singleInstance == null)
			singleInstance = new IntTypeEntry();
		
		return singleInstance;
	}
}

/**
 * 
 * Singleton class for the type entry for string
 *
 */

class StringTypeEntry extends TypeEntry
{
	private static StringTypeEntry singleInstance;
	
	private StringTypeEntry(){	}
	
	public static StringTypeEntry getInstance()
	{
		if(singleInstance == null)
			singleInstance = new StringTypeEntry();
		
		return singleInstance;
	}
}

/**
 * 
 * Singleton class for the type entry for void
 *
 */

class VoidTypeEntry extends TypeEntry
{
	private static VoidTypeEntry singleInstance;
	
	private VoidTypeEntry(){	}
	
	public static VoidTypeEntry getInstance()
	{
		if(singleInstance == null)
			singleInstance = new VoidTypeEntry();
		
		return singleInstance;
	}
}

class NilTypeEntry extends TypeEntry
{
	private static NilTypeEntry singleInstance;
	
	private NilTypeEntry(){	}
	
	public static NilTypeEntry getInstance()
	{
		if(singleInstance == null)
			singleInstance = new NilTypeEntry();
		
		return singleInstance;
	}
}

class RecordTypeEntry extends TypeEntry
{
	private Symbol fieldName;
	private TypeEntry fieldType;
	private RecordTypeEntry tail;
    
    public RecordTypeEntry(Symbol n, TypeEntry t, RecordTypeEntry x) 
    {
       fieldName = n; 
       fieldType = t;
       tail = x;
    }
    
    public boolean coerceTo(TypeEntry t) 
    {
    	return this == t.actual();
    }
    
    public void setTail(RecordTypeEntry rte)
    {
    	tail = rte;
    }
    
    public Symbol getFieldName()
    {
    	return fieldName;
    }
    
    public TypeEntry getFieldType()
    {
    	return fieldType;
    }
    
    public RecordTypeEntry getNext()
    {
    	return tail;
    }
}

class ArrayTypeEntry extends TypeEntry
{
	private  TypeEntry baseTypeEntry;
	
	public ArrayTypeEntry(TypeEntry e) 
	{
		baseTypeEntry = e;
	}
	
	public TypeEntry getBaseTypeEntry()
	{
		return baseTypeEntry;
	}
}

class NameTypeEntry extends TypeEntry
{
	public Symbol name;
	private TypeEntry binding;
    
	public NameTypeEntry(Symbol n) 
    {
    	name=n;
    }
	
	
	   
	public boolean isLoop() 
	{
		TypeEntry b = binding; 
	    boolean any;
	    binding=null;
	    if (b==null) any=true;
	    else if (b instanceof NameTypeEntry)
	    	any=((NameTypeEntry)b).isLoop();
	    else any=false;
	    
	    binding=b;
	    return any;
	 }
	     
	 public TypeEntry getBinding() 
	 {
		 return binding;
	 }

	 public boolean coerceTo(TypeEntry t)
	 {
		 // return this.actual().coerceTo(t);
		 // TODO - implement correctly
		 return false;
	 }
	 
	 public void bind(TypeEntry t) 
	 {
		 binding = t;
	 }
}

class VariableEntry extends Entry
{
	private TypeEntry type;
	private int localVariableNum;
	private Level varLevel;
	private int paramNum;
	private boolean isParam;
	private boolean inRegister;
	private Temp registerTemp;
	private boolean onHeap;
	
	/** Used for parameters of the function that are stored in positive offsets from $FP 
	 * OR
	 * for local variables
	 * @param t
	 * @param offSet
	 * @param level
	 */
	public VariableEntry(TypeEntry t, boolean isParam, int num, Level level)
	{
		type = t;
		varLevel = level;
		this.isParam = isParam;
		if(isParam)
			paramNum = num;
		else
			localVariableNum = num;
		inRegister = false;
		this.onHeap = false;
	}
	
	public VariableEntry(TypeEntry t,Temp temp, Level level)
	{
		inRegister = true;
		isParam = false;
		type = t;
		varLevel = level;
		registerTemp = temp;
		this.onHeap = false;
	}
	
	public VariableEntry(TypeEntry t, Level level)
	{
		inRegister = true;
		isParam = false;
		type = t;
		varLevel = level;
		registerTemp = null;
		this.onHeap = true;
	}
	
	public TypeEntry getType()
	{
		return type;
	}
	
	public int getLocalVariableNum()
	{
		return localVariableNum;
	}
	
	public Level getLevel()
	{
		return varLevel;
	}
	
	public boolean isParamVar()
	{
		return isParam;
	}
	
	public int getParamNum()
	{
		return paramNum;
	}

	public boolean isInRegister()
	{
		return inRegister;
	}
	
	public Temp getRegisterTemp() 
	{
		return registerTemp;
	}
	
	public void setRegisterTemp(Temp temp) 
	{
		registerTemp = temp;
	}

	public boolean isOnHeap() {
		return onHeap;
	}

}

class FunctionEntry extends Entry
{
	private RecordTypeEntry formals;          // The formal parameters to the function
	private TypeEntry result;                 // The return type of the function
	private Level level;  // The activation frame of the function
	
	public FunctionEntry(RecordTypeEntry formals, TypeEntry result,
			Level level)
	{
		this.formals = formals;
		this.result = result;
		this.level = level;
	}
	
	public RecordTypeEntry getFormals()
	{
		return formals;
	}
	
	public TypeEntry getReturnType()
	{
		return result;
	}
	
	public Level getLevel()
	{
		return level;
	}
}